/**
 * 
 */
package ua.lviv.franko.solvers.twoDimention;

import java.util.ArrayList;

import org.jzy3d.maths.Range;
import org.jzy3d.plot3d.builder.Mapper;

import ua.lviv.franko.integration.Gauss2D;
import ua.lviv.franko.integration.IFunction2D;
import ua.lviv.franko.solvers.ElementMatrix;
import ua.lviv.franko.solvers.IElement2D;
import ua.lviv.franko.triangulation.SaveStructures.Node;
import ua.lviv.franko.visualisation.Visualise3D;

/**
 * @author marko
 * 
 */
public class LinearRectangularElement implements IElement2D {

	public static int				QuadraturePointsNumber	= 20;
	private Gauss2D					gau;
	// problem data
	protected EquationParameters	eParams;

	// rectangle data
	protected Node					A;
	protected Node					B;
	protected Node					C;
	protected Node					D;

	public IFunction2D				N1;
	public IFunction2D				N2;
	public IFunction2D				N3;
	public IFunction2D				N4;

	// protected int ANum;
	// protected int BNum;
	// protected int CNum;
	// protected int DNum;
	ArrayList<Integer>				pos;
	// results
	protected ElementMatrix			data;
	double							a;
	double							b;

	public LinearRectangularElement(int ANum, int BNum, int CNum, int DNum, final Node A, final Node B, final Node C, final Node D, EquationParameters eParams) {
		this.eParams = eParams;
		pos = new ArrayList<Integer>();
		pos.add(ANum);
		pos.add(BNum);
		pos.add(CNum);
		pos.add(DNum);

		this.A = A;
		this.B = B;
		this.C = C;
		this.D = D;

		N1 = new IFunction2D() {

			@Override
			public double calculate(double x, double y) {
				return ((x - B.x) * (y - D.y)) / (A.x - B.x) / (A.y - D.y);
			}

			@Override
			public double calculateDerivatyX(double x, double y) {
				// return ( -1.0 )/Math.abs((A.x-B.x)/(A.y-D.y));
				return ((y - D.y)) / (A.x - B.x) / (A.y - D.y);
			}

			@Override
			public double calculateDerivatyY(double x, double y) {
				// return ( -1.0 )/Math.abs((A.x-B.x)/(A.y-D.y));
				return ((x - B.x)) / (A.x - B.x) / (A.y - D.y);
			}
		};
		N2 = new IFunction2D() {

			@Override
			public double calculate(double x, double y) {
				return ((x - A.x) * (y - C.y)) / (B.x - A.x) / (B.y - C.y);
			}

			@Override
			public double calculateDerivatyX(double x, double y) {
				// return ( 1.0 )/Math.abs((B.x-A.x)/(B.y-C.y));
				return ((y - C.y)) / (B.x - A.x) / (B.y - C.y);
			}

			@Override
			public double calculateDerivatyY(double x, double y) {
				// return ( -1.0 )/Math.abs((B.x-A.x)/(B.y-C.y));
				return ((x - A.x)) / (B.x - A.x) / (B.y - C.y);
			}
		};
		N3 = new IFunction2D() {

			@Override
			public double calculate(double x, double y) {
				return ((x - D.x) * (y - B.y)) / (C.x - D.x) / (C.y - B.y);
			}

			@Override
			public double calculateDerivatyX(double x, double y) {
				// return ( 1.0 )/Math.abs((C.x-D.x)/(C.y-B.y));
				return ((y - B.y)) / (C.x - D.x) / (C.y - B.y);
			}

			@Override
			public double calculateDerivatyY(double x, double y) {
				// return ( 1.0 )/Math.abs((C.x-D.x)/(C.y-B.y));
				return ((x - D.x)) / (C.x - D.x) / (C.y - B.y);
			}
		};
		N4 = new IFunction2D() {

			@Override
			public double calculate(double x, double y) {
				return ((x - C.x) * (y - A.y)) / (D.x - C.x) / (D.y - A.y);
			}

			@Override
			public double calculateDerivatyX(double x, double y) {
				// return ( -1.0 )/Math.abs((D.x-C.x)/(D.y-A.y));
				return ((y - A.y)) / (D.x - C.x) / (D.y - A.y);
			}

			@Override
			public double calculateDerivatyY(double x, double y) {
				// return ( 1.0 )/Math.abs((D.x-C.x)/(D.y-A.y));
				return ((x - C.x)) / (D.x - C.x) / (D.y - A.y);
			}
		};

		gau = new Gauss2D(QuadraturePointsNumber);
	}

	@Override
	public double calculate(double x, double y, double[] vals) {
		if (x > B.x || x < A.x || y > C.y || y < A.y) {
			return 0.0;
		}
		return N1.calculate(x, y) * vals[pos.get(0)] + N2.calculate(x, y) * vals[pos.get(1)] + N3.calculate(x, y) * vals[pos.get(2)] + N4.calculate(x, y) * vals[pos.get(3)];
	}

	@Override
	public void generateAll() {
		data = new ElementMatrix(pos);
		double left = A.x, right = B.x, top = C.y, bottom = A.y;

		// a = (top-bottom)/2.0;
		// b = (right-left)/2.0;

		// generate base functions of Q1

		// display(N1);
		// display(N2);
		// display(N3);
		// display(N4);
		ArrayList<IFunction2D> baseFunctions = new ArrayList<IFunction2D>();
		baseFunctions.add(N1);
		baseFunctions.add(N2);
		baseFunctions.add(N3);
		baseFunctions.add(N4);
		// calculate matrix and vector
		for (int i = 0; i < 4; i++) {
			IFunction2D f1 = baseFunctions.get(i);
			for (int j = 0; j < 4; j++) {
				IFunction2D f2 = baseFunctions.get(j);
				data.matrix[j][i] = calculateMatrix(f1, f2, left, right, bottom, top);
			}
			data.vector[i] = calculateVector(f1, left, right, bottom, top);
		}

	}

	public Node getA() {
		return A;
	}

	@Override
	public ElementMatrix getAll() {

		return data;
	}

	public Node getB() {
		return B;
	}

	public Node getC() {
		return C;
	}

	public Node getD() {
		return D;
	}

	@Override
	public ArrayList<Integer> getPos() {

		return pos;
	}

	private double calculateMatrix(final IFunction2D f1, final IFunction2D f2, double ax, double bx, double ay, double by) {
		return gau.integrate(ax, bx, ay, by, new IFunction2D() {

			@Override
			public double calculate(double x, double y) {

				return eParams.Kxx(x, y) * f1.calculateDerivatyX(x, y) * f2.calculateDerivatyX(x, y) + eParams.Kxy(x, y) * f1.calculateDerivatyX(x, y)
						* f2.calculateDerivatyY(x, y) + eParams.Kyx(x, y) * f1.calculateDerivatyY(x, y) * f2.calculateDerivatyX(x, y) + eParams.Kyy(x, y)
						* f1.calculateDerivatyY(x, y) * f2.calculateDerivatyY(x, y) + eParams.fPx.calculate(x, y) * f1.calculateDerivatyX(x, y) * f2.calculate(x, y)
						+ eParams.fPy.calculate(x, y) * f1.calculateDerivatyY(x, y) * f2.calculate(x, y) + eParams.fQ.calculate(x, y) * f1.calculate(x, y) * f2.calculate(x, y);
			}

			@Override
			public double calculateDerivatyX(double x, double y) {

				return 0;
			}

			@Override
			public double calculateDerivatyY(double x, double y) {

				return 0;
			}
		});
	}

	private double calculateVector(final IFunction2D f, double ax, double bx, double ay, double by) {
		return gau.integrate(ax, bx, ay, by, new IFunction2D() {

			@Override
			public double calculate(double x, double y) {

				return eParams.fF.calculate(x, y) * f.calculate(x, y);
			}

			@Override
			public double calculateDerivatyX(double x, double y) {

				return 0;
			}

			@Override
			public double calculateDerivatyY(double x, double y) {

				return 0;
			}
		});
	}

	protected void display(final IFunction2D ff) {
		Visualise3D.show(new Mapper() {
			@Override
			public double f(double x, double y) {
				// return ff.calculateDerivatyY(x, y);//(x, y);
				return ff.calculate(x, y);
			}
		}, new Range(A.x, C.x), new Range(A.y, C.y));
	}

}
